home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkMenubutton.c < prev    next >
C/C++ Source or Header  |  1995-06-09  |  40KB  |  1,237 lines

  1. /* 
  2.  * tkMenubutton.c --
  3.  *
  4.  *    This module implements button-like widgets that are used
  5.  *    to invoke pull-down menus.
  6.  *
  7.  * Copyright (c) 1990-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  */
  13.  
  14. static char sccsid[] = "@(#) tkMenubutton.c 1.70 95/06/09 08:20:02";
  15.  
  16. #include "tkPort.h"
  17. #include "default.h"
  18. #include "tkInt.h"
  19.  
  20. /*
  21.  * A data structure of the following type is kept for each
  22.  * widget managed by this file:
  23.  */
  24.  
  25. typedef struct {
  26.     Tk_Window tkwin;        /* Window that embodies the widget.  NULL
  27.                  * means that the window has been destroyed
  28.                  * but the data structures haven't yet been
  29.                  * cleaned up.*/
  30.     Display *display;        /* Display containing widget.  Needed, among
  31.                  * other things, so that resources can bee
  32.                  * freed up even after tkwin has gone away. */
  33.     Tcl_Interp *interp;        /* Interpreter associated with menubutton. */
  34.     Tcl_Command widgetCmd;    /* Token for menubutton's widget command. */
  35.     char *menuName;        /* Name of menu associated with widget.
  36.                  * Malloc-ed. */
  37.  
  38.     /*
  39.      * Information about what's displayed in the menu button:
  40.      */
  41.  
  42.     char *text;            /* Text to display in button (malloc'ed)
  43.                  * or NULL. */
  44.     int numChars;        /* # of characters in text. */
  45.     int underline;        /* Index of character to underline. */
  46.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  47.                  * If non-NULL, button displays the contents
  48.                  * of this variable. */
  49.     Pixmap bitmap;        /* Bitmap to display or None.  If not None
  50.                  * then text and textVar and underline
  51.                  * are ignored. */
  52.     char *imageString;        /* Name of image to display (malloc'ed), or
  53.                  * NULL.  If non-NULL, bitmap, text, and
  54.                  * textVarName are ignored. */
  55.     Tk_Image image;        /* Image to display in window, or NULL if
  56.                  * none. */
  57.  
  58.     /*
  59.      * Information used when displaying widget:
  60.      */
  61.  
  62.     Tk_Uid state;        /* State of button for display purposes:
  63.                  * normal, active, or disabled. */
  64.     Tk_3DBorder normalBorder;    /* Structure used to draw 3-D
  65.                  * border and background when window
  66.                  * isn't active.  NULL means no such
  67.                  * border exists. */
  68.     Tk_3DBorder activeBorder;    /* Structure used to draw 3-D
  69.                  * border and background when window
  70.                  * is active.  NULL means no such
  71.                  * border exists. */
  72.     int borderWidth;        /* Width of border. */
  73.     int relief;            /* 3-d effect: TK_RELIEF_RAISED, etc. */
  74.     int highlightWidth;        /* Width in pixels of highlight to draw
  75.                  * around widget when it has the focus.
  76.                  * <= 0 means don't draw a highlight. */
  77.     XColor *highlightBgColorPtr;
  78.                 /* Color for drawing traversal highlight
  79.                  * area when highlight is off. */
  80.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  81.     int inset;            /* Total width of all borders, including
  82.                  * traversal highlight and 3-D border.
  83.                  * Indicates how much interior stuff must
  84.                  * be offset from outside edges to leave
  85.                  * room for borders. */
  86.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  87.     XColor *normalFg;        /* Foreground color in normal mode. */
  88.     XColor *activeFg;        /* Foreground color in active mode.  NULL
  89.                  * means use normalFg instead. */
  90.     XColor *disabledFg;        /* Foreground color when disabled.  NULL
  91.                  * means use normalFg with a 50% stipple
  92.                  * instead. */
  93.     GC normalTextGC;        /* GC for drawing text in normal mode. */
  94.     GC activeTextGC;        /* GC for drawing text in active mode (NULL
  95.                  * means use normalTextGC). */
  96.     Pixmap gray;        /* Pixmap for displaying disabled text/icon if
  97.                  * disabledFg is NULL. */
  98.     GC disabledGC;        /* Used to produce disabled effect.  If
  99.                  * disabledFg isn't NULL, this GC is used to
  100.                  * draw button text or icon.  Otherwise
  101.                  * text or icon is drawn with normalGC and
  102.                  * this GC is used to stipple background
  103.                  * across it. */
  104.     int leftBearing;        /* Distance from text origin to leftmost drawn
  105.                  * pixel (positive means to right). */
  106.     int rightBearing;        /* Amount text sticks right from its origin. */
  107.     char *widthString;        /* Value of -width option.  Malloc'ed. */
  108.     char *heightString;        /* Value of -height option.  Malloc'ed. */
  109.     int width, height;        /* If > 0, these specify dimensions to request
  110.                  * for window, in characters for text and in
  111.                  * pixels for bitmaps.  In this case the actual
  112.                  * size of the text string or bitmap is
  113.                  * ignored in computing desired window size. */
  114.     int wrapLength;        /* Line length (in pixels) at which to wrap
  115.                  * onto next line.  <= 0 means don't wrap
  116.                  * except at newlines. */
  117.     int padX, padY;        /* Extra space around text or bitmap (pixels
  118.                  * on each side). */
  119.     Tk_Anchor anchor;        /* Where text/bitmap should be displayed
  120.                  * inside window region. */
  121.     Tk_Justify justify;        /* Justification to use for multi-line text. */
  122.     int textWidth;        /* Width needed to display text as requested,
  123.                  * in pixels. */
  124.     int textHeight;        /* Height needed to display text as requested,
  125.                  * in pixels. */
  126.     int indicatorOn;        /* Non-zero means display indicator;  0 means
  127.                  * don't display. */
  128.     int indicatorHeight;    /* Height of indicator in pixels.  This same
  129.                  * amount of extra space is also left on each
  130.                  * side of the indicator. 0 if no indicator. */
  131.     int indicatorWidth;        /* Width of indicator in pixels, including
  132.                  * indicatorHeight in padding on each side.
  133.                  * 0 if no indicator. */
  134.  
  135.     /*
  136.      * Miscellaneous information:
  137.      */
  138.  
  139.     Cursor cursor;        /* Current cursor for window, or None. */
  140.     char *takeFocus;        /* Value of -takefocus option;  not used in
  141.                  * the C code, but used by keyboard traversal
  142.                  * scripts.  Malloc'ed, but may be NULL. */
  143.     int flags;            /* Various flags;  see below for
  144.                  * definitions. */
  145. } MenuButton;
  146.  
  147. /*
  148.  * Flag bits for buttons:
  149.  *
  150.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  151.  *                has already been queued to redraw
  152.  *                this window.
  153.  * POSTED:            Non-zero means that the menu associated
  154.  *                with this button has been posted (typically
  155.  *                because of an active button press).
  156.  * GOT_FOCUS:            Non-zero means this button currently
  157.  *                has the input focus.
  158.  */
  159.  
  160. #define REDRAW_PENDING        1
  161. #define POSTED            2
  162. #define GOT_FOCUS        4
  163.  
  164. /*
  165.  * The following constants define the dimensions of the cascade indicator,
  166.  * which is displayed if the "-indicatoron" option is true.  The units for
  167.  * these options are 1/10 millimeters.
  168.  */
  169.  
  170. #define INDICATOR_WIDTH        40
  171. #define INDICATOR_HEIGHT    17
  172.  
  173. /*
  174.  * Information used for parsing configuration specs:
  175.  */
  176.  
  177. static Tk_ConfigSpec configSpecs[] = {
  178.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  179.     DEF_MENUBUTTON_ACTIVE_BG_COLOR, Tk_Offset(MenuButton, activeBorder),
  180.     TK_CONFIG_COLOR_ONLY},
  181.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  182.     DEF_MENUBUTTON_ACTIVE_BG_MONO, Tk_Offset(MenuButton, activeBorder),
  183.     TK_CONFIG_MONO_ONLY},
  184.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  185.     DEF_MENUBUTTON_ACTIVE_FG_COLOR, Tk_Offset(MenuButton, activeFg),
  186.     TK_CONFIG_COLOR_ONLY},
  187.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  188.     DEF_MENUBUTTON_ACTIVE_FG_MONO, Tk_Offset(MenuButton, activeFg),
  189.     TK_CONFIG_MONO_ONLY},
  190.     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
  191.     DEF_MENUBUTTON_ANCHOR, Tk_Offset(MenuButton, anchor), 0},
  192.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  193.     DEF_MENUBUTTON_BG_COLOR, Tk_Offset(MenuButton, normalBorder),
  194.     TK_CONFIG_COLOR_ONLY},
  195.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  196.     DEF_MENUBUTTON_BG_MONO, Tk_Offset(MenuButton, normalBorder),
  197.     TK_CONFIG_MONO_ONLY},
  198.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  199.     (char *) NULL, 0, 0},
  200.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  201.     (char *) NULL, 0, 0},
  202.     {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
  203.     DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap),
  204.     TK_CONFIG_NULL_OK},
  205.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  206.     DEF_MENUBUTTON_BORDER_WIDTH, Tk_Offset(MenuButton, borderWidth), 0},
  207.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  208.     DEF_MENUBUTTON_CURSOR, Tk_Offset(MenuButton, cursor),
  209.     TK_CONFIG_NULL_OK},
  210.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  211.     "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR,
  212.     Tk_Offset(MenuButton, disabledFg),
  213.     TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
  214.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  215.     "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_MONO,
  216.     Tk_Offset(MenuButton, disabledFg),
  217.     TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
  218.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  219.     (char *) NULL, 0, 0},
  220.     {TK_CONFIG_FONT, "-font", "font", "Font",
  221.     DEF_MENUBUTTON_FONT, Tk_Offset(MenuButton, fontPtr), 0},
  222.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  223.     DEF_MENUBUTTON_FG, Tk_Offset(MenuButton, normalFg), 0},
  224.     {TK_CONFIG_STRING, "-height", "height", "Height",
  225.     DEF_MENUBUTTON_HEIGHT, Tk_Offset(MenuButton, heightString), 0},
  226.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  227.     "HighlightBackground", DEF_MENUBUTTON_HIGHLIGHT_BG,
  228.     Tk_Offset(MenuButton, highlightBgColorPtr), 0},
  229.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  230.     DEF_MENUBUTTON_HIGHLIGHT, Tk_Offset(MenuButton, highlightColorPtr), 0},
  231.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  232.     "HighlightThickness", DEF_MENUBUTTON_HIGHLIGHT_WIDTH,
  233.     Tk_Offset(MenuButton, highlightWidth), 0},
  234.     {TK_CONFIG_STRING, "-image", "image", "Image",
  235.     DEF_MENUBUTTON_IMAGE, Tk_Offset(MenuButton, imageString),
  236.     TK_CONFIG_NULL_OK},
  237.     {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
  238.     DEF_MENUBUTTON_INDICATOR, Tk_Offset(MenuButton, indicatorOn), 0},
  239.     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
  240.     DEF_MENUBUTTON_JUSTIFY, Tk_Offset(MenuButton, justify), 0},
  241.     {TK_CONFIG_STRING, "-menu", "menu", "Menu",
  242.     DEF_MENUBUTTON_MENU, Tk_Offset(MenuButton, menuName),
  243.     TK_CONFIG_NULL_OK},
  244.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  245.     DEF_MENUBUTTON_PADX, Tk_Offset(MenuButton, padX), 0},
  246.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  247.     DEF_MENUBUTTON_PADY, Tk_Offset(MenuButton, padY), 0},
  248.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  249.     DEF_MENUBUTTON_RELIEF, Tk_Offset(MenuButton, relief), 0},
  250.     {TK_CONFIG_UID, "-state", "state", "State",
  251.     DEF_MENUBUTTON_STATE, Tk_Offset(MenuButton, state), 0},
  252.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  253.     DEF_MENUBUTTON_TAKE_FOCUS, Tk_Offset(MenuButton, takeFocus),
  254.     TK_CONFIG_NULL_OK},
  255.     {TK_CONFIG_STRING, "-text", "text", "Text",
  256.     DEF_MENUBUTTON_TEXT, Tk_Offset(MenuButton, text), 0},
  257.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  258.     DEF_MENUBUTTON_TEXT_VARIABLE, Tk_Offset(MenuButton, textVarName),
  259.     TK_CONFIG_NULL_OK},
  260.     {TK_CONFIG_INT, "-underline", "underline", "Underline",
  261.     DEF_MENUBUTTON_UNDERLINE, Tk_Offset(MenuButton, underline), 0},
  262.     {TK_CONFIG_STRING, "-width", "width", "Width",
  263.     DEF_MENUBUTTON_WIDTH, Tk_Offset(MenuButton, widthString), 0},
  264.     {TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
  265.     DEF_MENUBUTTON_WRAP_LENGTH, Tk_Offset(MenuButton, wrapLength), 0},
  266.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  267.     (char *) NULL, 0, 0}
  268. };
  269.  
  270. /*
  271.  * Forward declarations for procedures defined later in this file:
  272.  */
  273.  
  274. static void        ComputeMenuButtonGeometry _ANSI_ARGS_((
  275.                 MenuButton *mbPtr));
  276. static void        MenuButtonCmdDeletedProc _ANSI_ARGS_((
  277.                 ClientData clientData));
  278. static void        MenuButtonEventProc _ANSI_ARGS_((ClientData clientData,
  279.                 XEvent *eventPtr));
  280. static void        MenuButtonImageProc _ANSI_ARGS_((ClientData clientData,
  281.                 int x, int y, int width, int height, int imgWidth,
  282.                 int imgHeight));
  283. static char *        MenuButtonTextVarProc _ANSI_ARGS_((
  284.                 ClientData clientData, Tcl_Interp *interp,
  285.                 char *name1, char *name2, int flags));
  286. static int        MenuButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
  287.                 Tcl_Interp *interp, int argc, char **argv));
  288. static int        ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp,
  289.                 MenuButton *mbPtr, int argc, char **argv,
  290.                 int flags));
  291. static void        DestroyMenuButton _ANSI_ARGS_((ClientData clientData));
  292. static void        DisplayMenuButton _ANSI_ARGS_((ClientData clientData));
  293.  
  294. /*
  295.  *--------------------------------------------------------------
  296.  *
  297.  * Tk_MenubuttonCmd --
  298.  *
  299.  *    This procedure is invoked to process the "button", "label",
  300.  *    "radiobutton", and "checkbutton" Tcl commands.  See the
  301.  *    user documentation for details on what it does.
  302.  *
  303.  * Results:
  304.  *    A standard Tcl result.
  305.  *
  306.  * Side effects:
  307.  *    See the user documentation.
  308.  *
  309.  *--------------------------------------------------------------
  310.  */
  311.  
  312. int
  313. Tk_MenubuttonCmd(clientData, interp, argc, argv)
  314.     ClientData clientData;    /* Main window associated with
  315.                  * interpreter. */
  316.     Tcl_Interp *interp;        /* Current interpreter. */
  317.     int argc;            /* Number of arguments. */
  318.     char **argv;        /* Argument strings. */
  319. {
  320.     register MenuButton *mbPtr;
  321.     Tk_Window tkwin = (Tk_Window) clientData;
  322.     Tk_Window new;
  323.  
  324.     if (argc < 2) {
  325.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  326.         argv[0], " pathName ?options?\"", (char *) NULL);
  327.     return TCL_ERROR;
  328.     }
  329.  
  330.     /*
  331.      * Create the new window.
  332.      */
  333.  
  334.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  335.     if (new == NULL) {
  336.     return TCL_ERROR;
  337.     }
  338.  
  339.     /*
  340.      * Initialize the data structure for the button.
  341.      */
  342.  
  343.     mbPtr = (MenuButton *) ckalloc(sizeof(MenuButton));
  344.     mbPtr->tkwin = new;
  345.     mbPtr->display = Tk_Display (new);
  346.     mbPtr->interp = interp;
  347.     mbPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(mbPtr->tkwin),
  348.         MenuButtonWidgetCmd, (ClientData) mbPtr, MenuButtonCmdDeletedProc);
  349.     mbPtr->menuName = NULL;
  350.     mbPtr->text = NULL;
  351.     mbPtr->numChars = 0;
  352.     mbPtr->underline = -1;
  353.     mbPtr->textVarName = NULL;
  354.     mbPtr->bitmap = None;
  355.     mbPtr->imageString = NULL;
  356.     mbPtr->image = NULL;
  357.     mbPtr->state = tkNormalUid;
  358.     mbPtr->normalBorder = NULL;
  359.     mbPtr->activeBorder = NULL;
  360.     mbPtr->borderWidth = 0;
  361.     mbPtr->relief = TK_RELIEF_FLAT;
  362.     mbPtr->highlightWidth = 0;
  363.     mbPtr->highlightBgColorPtr = NULL;
  364.     mbPtr->highlightColorPtr = NULL;
  365.     mbPtr->inset = 0;
  366.     mbPtr->fontPtr = NULL;
  367.     mbPtr->normalFg = NULL;
  368.     mbPtr->activeFg = NULL;
  369.     mbPtr->disabledFg = NULL;
  370.     mbPtr->normalTextGC = None;
  371.     mbPtr->activeTextGC = None;
  372.     mbPtr->gray = None;
  373.     mbPtr->disabledGC = None;
  374.     mbPtr->leftBearing = 0;
  375.     mbPtr->rightBearing = 0;
  376.     mbPtr->widthString = NULL;
  377.     mbPtr->heightString = NULL;
  378.     mbPtr->width = 0;
  379.     mbPtr->width = 0;
  380.     mbPtr->wrapLength = 0;
  381.     mbPtr->padX = 0;
  382.     mbPtr->padY = 0;
  383.     mbPtr->anchor = TK_ANCHOR_CENTER;
  384.     mbPtr->justify = TK_JUSTIFY_CENTER;
  385.     mbPtr->indicatorOn = 0;
  386.     mbPtr->indicatorWidth = 0;
  387.     mbPtr->indicatorHeight = 0;
  388.     mbPtr->cursor = None;
  389.     mbPtr->takeFocus = NULL;
  390.     mbPtr->flags = 0;
  391.  
  392.     Tk_SetClass(mbPtr->tkwin, "Menubutton");
  393.     Tk_CreateEventHandler(mbPtr->tkwin,
  394.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  395.         MenuButtonEventProc, (ClientData) mbPtr);
  396.     if (ConfigureMenuButton(interp, mbPtr, argc-2, argv+2, 0) != TCL_OK) {
  397.     Tk_DestroyWindow(mbPtr->tkwin);
  398.     return TCL_ERROR;
  399.     }
  400.  
  401.     interp->result = Tk_PathName(mbPtr->tkwin);
  402.     return TCL_OK;
  403. }
  404.  
  405. /*
  406.  *--------------------------------------------------------------
  407.  *
  408.  * MenuButtonWidgetCmd --
  409.  *
  410.  *    This procedure is invoked to process the Tcl command
  411.  *    that corresponds to a widget managed by this module.
  412.  *    See the user documentation for details on what it does.
  413.  *
  414.  * Results:
  415.  *    A standard Tcl result.
  416.  *
  417.  * Side effects:
  418.  *    See the user documentation.
  419.  *
  420.  *--------------------------------------------------------------
  421.  */
  422.  
  423. static int
  424. MenuButtonWidgetCmd(clientData, interp, argc, argv)
  425.     ClientData clientData;    /* Information about button widget. */
  426.     Tcl_Interp *interp;        /* Current interpreter. */
  427.     int argc;            /* Number of arguments. */
  428.     char **argv;        /* Argument strings. */
  429. {
  430.     register MenuButton *mbPtr = (MenuButton *) clientData;
  431.     int result = TCL_OK;
  432.     size_t length;
  433.     int c;
  434.  
  435.     if (argc < 2) {
  436.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  437.         " option ?arg arg ...?\"", (char *) NULL);
  438.     return TCL_ERROR;
  439.     }
  440.     Tk_Preserve((ClientData) mbPtr);
  441.     c = argv[1][0];
  442.     length = strlen(argv[1]);
  443.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  444.         && (length >= 2)) {
  445.     if (argc != 3) {
  446.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  447.             argv[0], " cget option\"",
  448.             (char *) NULL);
  449.         goto error;
  450.     }
  451.     result = Tk_ConfigureValue(interp, mbPtr->tkwin, configSpecs,
  452.         (char *) mbPtr, argv[2], 0);
  453.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  454.         && (length >= 2)) {
  455.     if (argc == 2) {
  456.         result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
  457.             (char *) mbPtr, (char *) NULL, 0);
  458.     } else if (argc == 3) {
  459.         result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
  460.             (char *) mbPtr, argv[2], 0);
  461.     } else {
  462.         result = ConfigureMenuButton(interp, mbPtr, argc-2, argv+2,
  463.             TK_CONFIG_ARGV_ONLY);
  464.     }
  465.     } else {
  466.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  467.         "\":  must be cget or configure",
  468.         (char *) NULL);
  469.     goto error;
  470.     }
  471.     Tk_Release((ClientData) mbPtr);
  472.     return result;
  473.  
  474.     error:
  475.     Tk_Release((ClientData) mbPtr);
  476.     return TCL_ERROR;
  477. }
  478.  
  479. /*
  480.  *----------------------------------------------------------------------
  481.  *
  482.  * DestroyMenuButton --
  483.  *
  484.  *    This procedure is invoked to recycle all of the resources
  485.  *    associated with a button widget.  It is invoked as a
  486.  *    when-idle handler in order to make sure that there is no
  487.  *    other use of the button pending at the time of the deletion.
  488.  *
  489.  * Results:
  490.  *    None.
  491.  *
  492.  * Side effects:
  493.  *    Everything associated with the widget is freed up.
  494.  *
  495.  *----------------------------------------------------------------------
  496.  */
  497.  
  498. static void
  499. DestroyMenuButton(clientData)
  500.     ClientData clientData;    /* Info about button widget. */
  501. {
  502.     register MenuButton *mbPtr = (MenuButton *) clientData;
  503.  
  504.     /*
  505.      * Free up all the stuff that requires special handling, then
  506.      * let Tk_FreeOptions handle all the standard option-related
  507.      * stuff.
  508.      */
  509.  
  510.     if (mbPtr->textVarName != NULL) {
  511.     Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName,
  512.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  513.         MenuButtonTextVarProc, (ClientData) mbPtr);
  514.     }
  515.     if (mbPtr->image != NULL) {
  516.     Tk_FreeImage(mbPtr->image);
  517.     }
  518.     if (mbPtr->normalTextGC != None) {
  519.     Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
  520.     }
  521.     if (mbPtr->activeTextGC != None) {
  522.     Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
  523.     }
  524.     if (mbPtr->gray != None) {
  525.     Tk_FreeBitmap(mbPtr->display, mbPtr->gray);
  526.     }
  527.     if (mbPtr->disabledGC != None) {
  528.     Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
  529.     }
  530.     Tk_FreeOptions(configSpecs, (char *) mbPtr, mbPtr->display, 0);
  531.     ckfree((char *) mbPtr);
  532. }
  533.  
  534. /*
  535.  *----------------------------------------------------------------------
  536.  *
  537.  * ConfigureMenuButton --
  538.  *
  539.  *    This procedure is called to process an argv/argc list, plus
  540.  *    the Tk option database, in order to configure (or
  541.  *    reconfigure) a menubutton widget.
  542.  *
  543.  * Results:
  544.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  545.  *    returned, then interp->result contains an error message.
  546.  *
  547.  * Side effects:
  548.  *    Configuration information, such as text string, colors, font,
  549.  *    etc. get set for mbPtr;  old resources get freed, if there
  550.  *    were any.  The menubutton is redisplayed.
  551.  *
  552.  *----------------------------------------------------------------------
  553.  */
  554.  
  555. static int
  556. ConfigureMenuButton(interp, mbPtr, argc, argv, flags)
  557.     Tcl_Interp *interp;        /* Used for error reporting. */
  558.     register MenuButton *mbPtr;    /* Information about widget;  may or may
  559.                  * not already have values for some fields. */
  560.     int argc;            /* Number of valid entries in argv. */
  561.     char **argv;        /* Arguments. */
  562.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  563. {
  564.     XGCValues gcValues;
  565.     GC newGC;
  566.     unsigned long mask;
  567.     int result;
  568.     Tk_Image image;
  569.  
  570.     /*
  571.      * Eliminate any existing trace on variables monitored by the menubutton.
  572.      */
  573.  
  574.     if (mbPtr->textVarName != NULL) {
  575.     Tcl_UntraceVar(interp, mbPtr->textVarName,
  576.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  577.         MenuButtonTextVarProc, (ClientData) mbPtr);
  578.     }
  579.  
  580.     result = Tk_ConfigureWidget(interp, mbPtr->tkwin, configSpecs,
  581.         argc, argv, (char *) mbPtr, flags);
  582.     if (result != TCL_OK) {
  583.     return TCL_ERROR;
  584.     }
  585.  
  586.     /*
  587.      * A few options need special processing, such as setting the
  588.      * background from a 3-D border, or filling in complicated
  589.      * defaults that couldn't be specified to Tk_ConfigureWidget.
  590.      */
  591.  
  592.     if ((mbPtr->state == tkActiveUid) && !Tk_StrictMotif(mbPtr->tkwin)) {
  593.     Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
  594.     } else {
  595.     Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder);
  596.     if ((mbPtr->state != tkNormalUid) && (mbPtr->state != tkActiveUid)
  597.         && (mbPtr->state != tkDisabledUid)) {
  598.         Tcl_AppendResult(interp, "bad state value \"", mbPtr->state,
  599.             "\":  must be normal, active, or disabled", (char *) NULL);
  600.         mbPtr->state = tkNormalUid;
  601.         return TCL_ERROR;
  602.     }
  603.     }
  604.  
  605.     if (mbPtr->highlightWidth < 0) {
  606.     mbPtr->highlightWidth = 0;
  607.     }
  608.  
  609.     gcValues.font = mbPtr->fontPtr->fid;
  610.     gcValues.foreground = mbPtr->normalFg->pixel;
  611.     gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
  612.  
  613.     /*
  614.      * Note: GraphicsExpose events are disabled in GC's because they're
  615.      * used to copy stuff from an off-screen pixmap onto the screen (we know
  616.      * that there's no problem with obscured areas).
  617.      */
  618.  
  619.     gcValues.graphics_exposures = False;
  620.     newGC = Tk_GetGC(mbPtr->tkwin,
  621.         GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues);
  622.     if (mbPtr->normalTextGC != None) {
  623.     Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
  624.     }
  625.     mbPtr->normalTextGC = newGC;
  626.  
  627.     gcValues.font = mbPtr->fontPtr->fid;
  628.     gcValues.foreground = mbPtr->activeFg->pixel;
  629.     gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel;
  630.     newGC = Tk_GetGC(mbPtr->tkwin, GCForeground|GCBackground|GCFont,
  631.         &gcValues);
  632.     if (mbPtr->activeTextGC != None) {
  633.     Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
  634.     }
  635.     mbPtr->activeTextGC = newGC;
  636.  
  637.     gcValues.font = mbPtr->fontPtr->fid;
  638.     gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
  639.     if (mbPtr->disabledFg != NULL) {
  640.     gcValues.foreground = mbPtr->disabledFg->pixel;
  641.     mask = GCForeground|GCBackground|GCFont;
  642.     } else {
  643.     gcValues.foreground = gcValues.background;
  644.     if (mbPtr->gray == None) {
  645.         mbPtr->gray = Tk_GetBitmap(interp, mbPtr->tkwin,
  646.             Tk_GetUid("gray50"));
  647.         if (mbPtr->gray == None) {
  648.         return TCL_ERROR;
  649.         }
  650.     }
  651.     gcValues.fill_style = FillStippled;
  652.     gcValues.stipple = mbPtr->gray;
  653.     mask = GCForeground|GCFillStyle|GCStipple;
  654.     }
  655.     newGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
  656.     if (mbPtr->disabledGC != None) {
  657.     Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
  658.     }
  659.     mbPtr->disabledGC = newGC;
  660.  
  661.     if (mbPtr->padX < 0) {
  662.     mbPtr->padX = 0;
  663.     }
  664.     if (mbPtr->padY < 0) {
  665.     mbPtr->padY = 0;
  666.     }
  667.  
  668.     /*
  669.      * Get the image for the widget, if there is one.  Allocate the
  670.      * new image before freeing the old one, so that the reference
  671.      * count doesn't go to zero and cause image data to be discarded.
  672.      */
  673.  
  674.     if (mbPtr->imageString != NULL) {
  675.     image = Tk_GetImage(mbPtr->interp, mbPtr->tkwin,
  676.         mbPtr->imageString, MenuButtonImageProc, (ClientData) mbPtr);
  677.     if (image == NULL) {
  678.         return TCL_ERROR;
  679.     }
  680.     } else {
  681.     image = NULL;
  682.     }
  683.     if (mbPtr->image != NULL) {
  684.     Tk_FreeImage(mbPtr->image);
  685.     }
  686.     mbPtr->image = image;
  687.  
  688.     if ((mbPtr->image == NULL) && (mbPtr->bitmap == None)
  689.         && (mbPtr->textVarName != NULL)) {
  690.     /*
  691.      * The menubutton displays a variable.  Set up a trace to watch
  692.      * for any changes in it.
  693.      */
  694.  
  695.     char *value;
  696.  
  697.     value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
  698.     if (value == NULL) {
  699.         Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
  700.             TCL_GLOBAL_ONLY);
  701.     } else {
  702.         if (mbPtr->text != NULL) {
  703.         ckfree(mbPtr->text);
  704.         }
  705.         mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  706.         strcpy(mbPtr->text, value);
  707.     }
  708.     Tcl_TraceVar(interp, mbPtr->textVarName,
  709.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  710.         MenuButtonTextVarProc, (ClientData) mbPtr);
  711.     }
  712.  
  713.     /*
  714.      * Recompute the geometry for the button.
  715.      */
  716.  
  717.     if ((mbPtr->bitmap != None) || (mbPtr->image != NULL)) {
  718.     if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->widthString,
  719.         &mbPtr->width) != TCL_OK) {
  720.         widthError:
  721.         Tcl_AddErrorInfo(interp, "\n    (processing -width option)");
  722.         return TCL_ERROR;
  723.     }
  724.     if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->heightString,
  725.         &mbPtr->height) != TCL_OK) {
  726.         heightError:
  727.         Tcl_AddErrorInfo(interp, "\n    (processing -height option)");
  728.         return TCL_ERROR;
  729.     }
  730.     } else {
  731.     if (Tcl_GetInt(interp, mbPtr->widthString, &mbPtr->width)
  732.         != TCL_OK) {
  733.         goto widthError;
  734.     }
  735.     if (Tcl_GetInt(interp, mbPtr->heightString, &mbPtr->height)
  736.         != TCL_OK) {
  737.         goto heightError;
  738.     }
  739.     }
  740.     ComputeMenuButtonGeometry(mbPtr);
  741.  
  742.     /*
  743.      * Lastly, arrange for the button to be redisplayed.
  744.      */
  745.  
  746.     if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
  747.     Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  748.     mbPtr->flags |= REDRAW_PENDING;
  749.     }
  750.  
  751.     return TCL_OK;
  752. }
  753.  
  754. /*
  755.  *----------------------------------------------------------------------
  756.  *
  757.  * DisplayMenuButton --
  758.  *
  759.  *    This procedure is invoked to display a menubutton widget.
  760.  *
  761.  * Results:
  762.  *    None.
  763.  *
  764.  * Side effects:
  765.  *    Commands are output to X to display the menubutton in its
  766.  *    current mode.
  767.  *
  768.  *----------------------------------------------------------------------
  769.  */
  770.  
  771. static void
  772. DisplayMenuButton(clientData)
  773.     ClientData clientData;    /* Information about widget. */
  774. {
  775.     register MenuButton *mbPtr = (MenuButton *) clientData;
  776.     GC gc;
  777.     Tk_3DBorder border;
  778.     Pixmap pixmap;
  779.     int x = 0;            /* Initialization needed only to stop
  780.                  * compiler warning. */
  781.     int y;
  782.     register Tk_Window tkwin = mbPtr->tkwin;
  783.     int width, height;
  784.  
  785.     mbPtr->flags &= ~REDRAW_PENDING;
  786.     if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  787.     return;
  788.     }
  789.  
  790.     if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg != NULL)) {
  791.     gc = mbPtr->disabledGC;
  792.     border = mbPtr->normalBorder;
  793.     } else if ((mbPtr->state == tkActiveUid) && !Tk_StrictMotif(mbPtr->tkwin)) {
  794.     gc = mbPtr->activeTextGC;
  795.     border = mbPtr->activeBorder;
  796.     } else {
  797.     gc = mbPtr->normalTextGC;
  798.     border = mbPtr->normalBorder;
  799.     }
  800.  
  801.     /*
  802.      * In order to avoid screen flashes, this procedure redraws
  803.      * the menu button in a pixmap, then copies the pixmap to the
  804.      * screen in a single operation.  This means that there's no
  805.      * point in time where the on-sreen image has been cleared.
  806.      */
  807.  
  808.     pixmap = Tk_GetPixmap(mbPtr->display, Tk_WindowId(tkwin),
  809.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  810.     Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
  811.         Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  812.  
  813.     /*
  814.      * Display image or bitmap or text for button.
  815.      */
  816.  
  817.     if (mbPtr->image != None) {
  818.     Tk_SizeOfImage(mbPtr->image, &width, &height);
  819.  
  820.     imageOrBitmap:
  821.     switch (mbPtr->anchor) {
  822.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  823.         x += mbPtr->inset;
  824.         break;
  825.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  826.         x += ((int) (Tk_Width(tkwin) - width
  827.             - mbPtr->indicatorWidth))/2;
  828.         break;
  829.         default:
  830.         x += Tk_Width(tkwin) - mbPtr->inset - width
  831.             - mbPtr->indicatorWidth;
  832.         break;
  833.     }
  834.     switch (mbPtr->anchor) {
  835.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  836.         y = mbPtr->inset;
  837.         break;
  838.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  839.         y = ((int) (Tk_Height(tkwin) - height))/2;
  840.         break;
  841.         default:
  842.         y = Tk_Height(tkwin) - mbPtr->inset - height;
  843.         break;
  844.     }
  845.     if (mbPtr->image != NULL) {
  846.         Tk_RedrawImage(mbPtr->image, 0, 0, width, height, pixmap,
  847.             x, y);
  848.     } else {
  849.         XCopyPlane(mbPtr->display, mbPtr->bitmap, pixmap,
  850.             gc, 0, 0, (unsigned) width, (unsigned) height, x, y, 1);
  851.     }
  852.     } else if (mbPtr->bitmap != None) {
  853.     Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
  854.     goto imageOrBitmap;
  855.     } else {
  856.     width = mbPtr->textWidth;
  857.     height = mbPtr->textHeight;
  858.     switch (mbPtr->anchor) {
  859.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  860.         x = mbPtr->inset + mbPtr->padX;
  861.         break;
  862.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  863.         x = ((int) (Tk_Width(tkwin) - width
  864.             - mbPtr->indicatorWidth))/2;
  865.         break;
  866.         default:
  867.         x = Tk_Width(tkwin) - width - mbPtr->padX - mbPtr->inset
  868.             - mbPtr->indicatorWidth;
  869.         break;
  870.     }
  871.     switch (mbPtr->anchor) {
  872.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  873.         y = mbPtr->inset + mbPtr->padY;
  874.         break;
  875.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  876.         y = ((int) (Tk_Height(tkwin) - height))/2;
  877.         break;
  878.         default:
  879.         y = Tk_Height(tkwin) - mbPtr->inset - mbPtr->padY - height;
  880.         break;
  881.     }
  882.     TkDisplayText(mbPtr->display, pixmap, mbPtr->fontPtr,
  883.         mbPtr->text, mbPtr->numChars, x, y, mbPtr->textWidth,
  884.         mbPtr->justify, mbPtr->underline, gc);
  885.     }
  886.  
  887.     /*
  888.      * If the menu button is disabled with a stipple rather than a special
  889.      * foreground color, generate the stippled effect.
  890.      */
  891.  
  892.     if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg == NULL)) {
  893.     XFillRectangle(mbPtr->display, pixmap, mbPtr->disabledGC,
  894.         mbPtr->inset, mbPtr->inset,
  895.         (unsigned) (Tk_Width(tkwin) - 2*mbPtr->inset),
  896.         (unsigned) (Tk_Height(tkwin) - 2*mbPtr->inset));
  897.     }
  898.  
  899.     /*
  900.      * Draw the cascade indicator for the menu button on the
  901.      * right side of the window, if desired.
  902.      */
  903.  
  904.     if (mbPtr->indicatorOn) {
  905.     int borderWidth;
  906.  
  907.     borderWidth = (mbPtr->indicatorHeight+1)/3;
  908.     if (borderWidth < 1) {
  909.         borderWidth = 1;
  910.     }
  911.     Tk_Fill3DRectangle(tkwin, pixmap, border,
  912.         Tk_Width(tkwin) - mbPtr->inset - mbPtr->indicatorWidth
  913.             + mbPtr->indicatorHeight,
  914.         y + ((int) (height - mbPtr->indicatorHeight))/2,
  915.         mbPtr->indicatorWidth - 2*mbPtr->indicatorHeight,
  916.         mbPtr->indicatorHeight, borderWidth, TK_RELIEF_RAISED);
  917.     }
  918.  
  919.     /*
  920.      * Draw the border and traversal highlight last.  This way, if the
  921.      * menu button's contents overflow onto the border they'll be covered
  922.      * up by the border.
  923.      */
  924.  
  925.     if (mbPtr->relief != TK_RELIEF_FLAT) {
  926.     Tk_Draw3DRectangle(tkwin, pixmap, border,
  927.         mbPtr->highlightWidth, mbPtr->highlightWidth,
  928.         Tk_Width(tkwin) - 2*mbPtr->highlightWidth,
  929.         Tk_Height(tkwin) - 2*mbPtr->highlightWidth,
  930.         mbPtr->borderWidth, mbPtr->relief);
  931.     }
  932.     if (mbPtr->highlightWidth != 0) {
  933.     GC gc;
  934.  
  935.     if (mbPtr->flags & GOT_FOCUS) {
  936.         gc = Tk_GCForColor(mbPtr->highlightColorPtr, pixmap);
  937.     } else {
  938.         gc = Tk_GCForColor(mbPtr->highlightBgColorPtr, pixmap);
  939.     }
  940.     Tk_DrawFocusHighlight(tkwin, gc, mbPtr->highlightWidth, pixmap);
  941.     }
  942.  
  943.     /*
  944.      * Copy the information from the off-screen pixmap onto the screen,
  945.      * then delete the pixmap.
  946.      */
  947.  
  948.     XCopyArea(mbPtr->display, pixmap, Tk_WindowId(tkwin),
  949.         mbPtr->normalTextGC, 0, 0, (unsigned) Tk_Width(tkwin),
  950.         (unsigned) Tk_Height(tkwin), 0, 0);
  951.     Tk_FreePixmap(mbPtr->display, pixmap);
  952. }
  953.  
  954. /*
  955.  *--------------------------------------------------------------
  956.  *
  957.  * MenuButtonEventProc --
  958.  *
  959.  *    This procedure is invoked by the Tk dispatcher for various
  960.  *    events on buttons.
  961.  *
  962.  * Results:
  963.  *    None.
  964.  *
  965.  * Side effects:
  966.  *    When the window gets deleted, internal structures get
  967.  *    cleaned up.  When it gets exposed, it is redisplayed.
  968.  *
  969.  *--------------------------------------------------------------
  970.  */
  971.  
  972. static void
  973. MenuButtonEventProc(clientData, eventPtr)
  974.     ClientData clientData;    /* Information about window. */
  975.     XEvent *eventPtr;        /* Information about event. */
  976. {
  977.     MenuButton *mbPtr = (MenuButton *) clientData;
  978.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  979.     goto redraw;
  980.     } else if (eventPtr->type == ConfigureNotify) {
  981.     /*
  982.      * Must redraw after size changes, since layout could have changed
  983.      * and borders will need to be redrawn.
  984.      */
  985.  
  986.     goto redraw;
  987.     } else if (eventPtr->type == DestroyNotify) {
  988.     if (mbPtr->tkwin != NULL) {
  989.         mbPtr->tkwin = NULL;
  990.         Tcl_DeleteCommand(mbPtr->interp,
  991.             Tcl_GetCommandName(mbPtr->interp, mbPtr->widgetCmd));
  992.     }
  993.     if (mbPtr->flags & REDRAW_PENDING) {
  994.         Tk_CancelIdleCall(DisplayMenuButton, (ClientData) mbPtr);
  995.     }
  996.     Tk_EventuallyFree((ClientData) mbPtr, DestroyMenuButton);
  997.     } else if (eventPtr->type == FocusIn) {
  998.     if (eventPtr->xfocus.detail != NotifyInferior) {
  999.         mbPtr->flags |= GOT_FOCUS;
  1000.         if (mbPtr->highlightWidth > 0) {
  1001.         goto redraw;
  1002.         }
  1003.     }
  1004.     } else if (eventPtr->type == FocusOut) {
  1005.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1006.         mbPtr->flags &= ~GOT_FOCUS;
  1007.         if (mbPtr->highlightWidth > 0) {
  1008.         goto redraw;
  1009.         }
  1010.     }
  1011.     }
  1012.     return;
  1013.  
  1014.     redraw:
  1015.     if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) {
  1016.     Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  1017.     mbPtr->flags |= REDRAW_PENDING;
  1018.     }
  1019. }
  1020.  
  1021. /*
  1022.  *----------------------------------------------------------------------
  1023.  *
  1024.  * MenuButtonCmdDeletedProc --
  1025.  *
  1026.  *    This procedure is invoked when a widget command is deleted.  If
  1027.  *    the widget isn't already in the process of being destroyed,
  1028.  *    this command destroys it.
  1029.  *
  1030.  * Results:
  1031.  *    None.
  1032.  *
  1033.  * Side effects:
  1034.  *    The widget is destroyed.
  1035.  *
  1036.  *----------------------------------------------------------------------
  1037.  */
  1038.  
  1039. static void
  1040. MenuButtonCmdDeletedProc(clientData)
  1041.     ClientData clientData;    /* Pointer to widget record for widget. */
  1042. {
  1043.     MenuButton *mbPtr = (MenuButton *) clientData;
  1044.     Tk_Window tkwin = mbPtr->tkwin;
  1045.  
  1046.     /*
  1047.      * This procedure could be invoked either because the window was
  1048.      * destroyed and the command was then deleted (in which case tkwin
  1049.      * is NULL) or because the command was deleted, and then this procedure
  1050.      * destroys the widget.
  1051.      */
  1052.  
  1053.     if (tkwin != NULL) {
  1054.     mbPtr->tkwin = NULL;
  1055.     Tk_DestroyWindow(tkwin);
  1056.     }
  1057. }
  1058.  
  1059. /*
  1060.  *----------------------------------------------------------------------
  1061.  *
  1062.  * ComputeMenuButtonGeometry --
  1063.  *
  1064.  *    After changes in a menu button's text or bitmap, this procedure
  1065.  *    recomputes the menu button's geometry and passes this information
  1066.  *    along to the geometry manager for the window.
  1067.  *
  1068.  * Results:
  1069.  *    None.
  1070.  *
  1071.  * Side effects:
  1072.  *    The menu button's window may change size.
  1073.  *
  1074.  *----------------------------------------------------------------------
  1075.  */
  1076.  
  1077. static void
  1078. ComputeMenuButtonGeometry(mbPtr)
  1079.     register MenuButton *mbPtr;        /* Widget record for menu button. */
  1080. {
  1081.     int width, height, mm, pixels;
  1082.  
  1083.     mbPtr->inset = mbPtr->highlightWidth + mbPtr->borderWidth;
  1084.     if (mbPtr->image != None) {
  1085.     Tk_SizeOfImage(mbPtr->image, &width, &height);
  1086.     if (mbPtr->width > 0) {
  1087.         width = mbPtr->width;
  1088.     }
  1089.     if (mbPtr->height > 0) {
  1090.         height = mbPtr->height;
  1091.     }
  1092.     } else if (mbPtr->bitmap != None) {
  1093.     Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
  1094.     if (mbPtr->width > 0) {
  1095.         width = mbPtr->width;
  1096.     }
  1097.     if (mbPtr->height > 0) {
  1098.         height = mbPtr->height;
  1099.     }
  1100.     } else {
  1101.     mbPtr->numChars = strlen(mbPtr->text);
  1102.     TkComputeTextGeometry(mbPtr->fontPtr, mbPtr->text,
  1103.         mbPtr->numChars, mbPtr->wrapLength, &mbPtr->textWidth,
  1104.         &mbPtr->textHeight);
  1105.     width = mbPtr->textWidth;
  1106.     height = mbPtr->textHeight;
  1107.     if (mbPtr->width > 0) {
  1108.         width = mbPtr->width * XTextWidth(mbPtr->fontPtr, "0", 1);
  1109.     }
  1110.     if (mbPtr->height > 0) {
  1111.         height = mbPtr->height * (mbPtr->fontPtr->ascent
  1112.             + mbPtr->fontPtr->descent);
  1113.     }
  1114.     width += 2*mbPtr->padX;
  1115.     height += 2*mbPtr->padY;
  1116.     }
  1117.  
  1118.     if (mbPtr->indicatorOn) {
  1119.     mm = WidthMMOfScreen(Tk_Screen(mbPtr->tkwin));
  1120.     pixels = WidthOfScreen(Tk_Screen(mbPtr->tkwin));
  1121.     mbPtr->indicatorHeight= (INDICATOR_HEIGHT * pixels)/(10*mm);
  1122.     mbPtr->indicatorWidth = (INDICATOR_WIDTH * pixels)/(10*mm)
  1123.         + 2*mbPtr->indicatorHeight;
  1124.     width += mbPtr->indicatorWidth;
  1125.     } else {
  1126.     mbPtr->indicatorHeight = 0;
  1127.     mbPtr->indicatorWidth = 0;
  1128.     }
  1129.  
  1130.     Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->inset),
  1131.         (int) (height + 2*mbPtr->inset));
  1132.     Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->inset);
  1133. }
  1134.  
  1135. /*
  1136.  *--------------------------------------------------------------
  1137.  *
  1138.  * MenuButtonTextVarProc --
  1139.  *
  1140.  *    This procedure is invoked when someone changes the variable
  1141.  *    whose contents are to be displayed in a menu button.
  1142.  *
  1143.  * Results:
  1144.  *    NULL is always returned.
  1145.  *
  1146.  * Side effects:
  1147.  *    The text displayed in the menu button will change to match the
  1148.  *    variable.
  1149.  *
  1150.  *--------------------------------------------------------------
  1151.  */
  1152.  
  1153.     /* ARGSUSED */
  1154. static char *
  1155. MenuButtonTextVarProc(clientData, interp, name1, name2, flags)
  1156.     ClientData clientData;    /* Information about button. */
  1157.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1158.     char *name1;        /* Name of variable. */
  1159.     char *name2;        /* Second part of variable name. */
  1160.     int flags;            /* Information about what happened. */
  1161. {
  1162.     register MenuButton *mbPtr = (MenuButton *) clientData;
  1163.     char *value;
  1164.  
  1165.     /*
  1166.      * If the variable is unset, then immediately recreate it unless
  1167.      * the whole interpreter is going away.
  1168.      */
  1169.  
  1170.     if (flags & TCL_TRACE_UNSETS) {
  1171.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1172.         Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
  1173.             TCL_GLOBAL_ONLY);
  1174.         Tcl_TraceVar(interp, mbPtr->textVarName,
  1175.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1176.             MenuButtonTextVarProc, clientData);
  1177.     }
  1178.     return (char *) NULL;
  1179.     }
  1180.  
  1181.     value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
  1182.     if (value == NULL) {
  1183.     value = "";
  1184.     }
  1185.     if (mbPtr->text != NULL) {
  1186.     ckfree(mbPtr->text);
  1187.     }
  1188.     mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  1189.     strcpy(mbPtr->text, value);
  1190.     ComputeMenuButtonGeometry(mbPtr);
  1191.  
  1192.     if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin)
  1193.         && !(mbPtr->flags & REDRAW_PENDING)) {
  1194.     Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  1195.     mbPtr->flags |= REDRAW_PENDING;
  1196.     }
  1197.     return (char *) NULL;
  1198. }
  1199.  
  1200. /*
  1201.  *----------------------------------------------------------------------
  1202.  *
  1203.  * MenuButtonImageProc --
  1204.  *
  1205.  *    This procedure is invoked by the image code whenever the manager
  1206.  *    for an image does something that affects the size of contents
  1207.  *    of an image displayed in a button.
  1208.  *
  1209.  * Results:
  1210.  *    None.
  1211.  *
  1212.  * Side effects:
  1213.  *    Arranges for the button to get redisplayed.
  1214.  *
  1215.  *----------------------------------------------------------------------
  1216.  */
  1217.  
  1218. static void
  1219. MenuButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
  1220.     ClientData clientData;        /* Pointer to widget record. */
  1221.     int x, y;                /* Upper left pixel (within image)
  1222.                      * that must be redisplayed. */
  1223.     int width, height;            /* Dimensions of area to redisplay
  1224.                      * (may be <= 0). */
  1225.     int imgWidth, imgHeight;        /* New dimensions of image. */
  1226. {
  1227.     register MenuButton *mbPtr = (MenuButton *) clientData;
  1228.  
  1229.     if (mbPtr->tkwin != NULL) {
  1230.     ComputeMenuButtonGeometry(mbPtr);
  1231.     if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
  1232.         Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  1233.         mbPtr->flags |= REDRAW_PENDING;
  1234.     }
  1235.     }
  1236. }
  1237.